//============================================================================
// Name        : tp1.cpp
// Author      : Sergio Colinas
// Version     :
// Copyright   : 
// Description : Trabajo Práctico Nro 1
//============================================================================

#include "tp1.h"

#define USER_FILE "usuario.txt"
#define CHAT_FILE "conversacion.txt"

int main(int argc, char** argv) {
	unsigned int debug = 0;

	Logger *log = NULL;
	LockFile *chatFileLock = NULL;
	User *user = NULL;

	//Parseo los parámetros que recibe el programa
	if (parse_command_line(argc, argv, &debug) != 0) {
		return 1;
	} else {
		try {
			//Obtengo la instancia del Logger (le paso el flag de debug) e instancio un lock para conversacion.txt
			log = Logger::Instance(debug != 0);
			chatFileLock = new LockFile((char*) CHAT_FILE, debug != 0);
			/*********** INGRESO A LA SALA DE CHAT CON UN ALIAS UNICO ************/
			cout << "Bienvenido a ConcuConversation (ingrese salir para terminar)" << endl;
			string tmp_alias;
			cout << "Ingrese su apodo: ";
			cin >> tmp_alias;
			Util::findAndReplace(tmp_alias, "*@*", "");
			user = new User(string(USER_FILE), debug != 0);
			while (!user->entrar_al_chat(tmp_alias)) {
				cout << "Ya existe el apodo. Ingrese otro: ";
				cin >> tmp_alias;
				Util::findAndReplace(tmp_alias, "*@*", "");
			}
			pid_t id = fork();
			if (id == 0) {
				/************** PROCESO LECTOR **************/
				log->writeToLogFile("Comienzo proceso lector\n");
				//Abro el archivo para leer solamente
				int fd = open((char*) CHAT_FILE, O_CREAT | O_RDONLY, 0777);
				if (fd < 0) {
					cerr << "Se produjo un error al intentar abrir el archivo " << CHAT_FILE << endl;
					return 1;
				} else {
					//Me posiciono al final del archivo
					if (lseek(fd, 0, SEEK_END) == -1) {
						cerr << "Se produjo un error al intentar posicionarse al final del archivo " << CHAT_FILE << endl;
						return 1;
					} else {
						char buffer[1];
						string line = "";
						while (true) {
							//Leo de a un caracter
							ssize_t read_bytes = read(fd, buffer, 1);
							if (read_bytes > 0) {
								line += buffer[0];
								//Hasta encontrar un \n que me delimita cada mensaje
								if (buffer[0] == '\n') {
									//Solo muestro los mensajes de otros usuarios y no los propios
									if (line.find(user->get_alias() + "*@*") != 0) {
										Util::findAndReplace(line, "*@*", "");
										cout << line;
										//La condición para dejar de leer es que el usuario salga de la sala
									} else if (line.find(user->get_alias() + "*@* salió") == 0)
										break;
									line.clear();
								}
							} else if (read_bytes == -1) {
								cerr << "Se produjo un error al intentar leer el archivo " << CHAT_FILE << endl;
							}
						}
					}
					close(fd);
					log->writeToLogFile("Fin proceso lector\n");
				}
			} else {
				/************** PROCESO ESCRITOR **************/
				log->writeToLogFile("Comienzo proceso escritor\n");
				//Utilizo locks para sincronizar el acceso al archivo de conversacion
				string msg = user->get_alias() + "*@* entró en la sala de chat.\n";
				chatFileLock->tomarLock();
				chatFileLock->escribir(msg.c_str(), msg.size());
				chatFileLock->liberarLock();
				getline(cin, msg, '\n');
				Util::findAndReplace(msg, "*@*", "");
				//Mientras el usuario no ingrese la palabra salir, escribo el mensaje en el archivo...
				while ((msg != "salir") && (msg != "Salir") && (msg != "SALIR")) {
					if (msg != "") {
						msg = user->get_alias() + "*@* dijo el " + Util::getAsciiTime() + msg + '\n';
						chatFileLock->tomarLock();
						chatFileLock->escribir(msg.c_str(), msg.size());
						chatFileLock->liberarLock();
					}
					getline(cin, msg, '\n');
				}
				/************ SALIDA DE LA SALA DE CHAT *************/
				int usuarios = user->salir_del_chat();

				msg = user->get_alias() + "*@* salió de la sala de chat.\n";
				chatFileLock->tomarLock();
				chatFileLock->escribir(msg.c_str(), msg.size());
				chatFileLock->liberarLock();

				// renombro el archivo de la conversación si corresponde (si no queda nadie en la sala)
				if (usuarios == 0) {
					srand(time(0));
					string new_name = CHAT_FILE;
					new_name.erase(new_name.end() - 4, new_name.end());
					new_name.append("_").append(Util::toString(rand())).append(".txt");
					rename(CHAT_FILE, new_name.c_str());
				}

			}
			log->writeToLogFile("Fin proceso escritor\n");
			if (user != NULL)
				delete user;
			if (chatFileLock != NULL)
				delete chatFileLock;
		} catch (const char* e) {
			cerr << e << endl;

			if (user != NULL)
				delete user;
			if (chatFileLock != NULL)
				delete chatFileLock;

			return 1;
		} catch (...) {
			cerr << "error desconocido." << endl;
			if (user != NULL)
				delete user;
			if (chatFileLock != NULL)
				delete chatFileLock;

			return 1;
		}
	}

	return 0;
}

void usage() {
	cout << "Usage:" << endl;
	cout << "\t" << "tp1 -h" << endl;
	cout << "\t" << "tp1 -V" << endl;
	cout << "\t" << "tp1 [options]" << endl;
	cout << "Options:" << endl;
	cout << "\t" << "-h, --help" << "\t" << "Prints usage information." << endl;
	cout << "\t" << "-V, --version" << "\t" << "Prints version information." << endl;
	cout << "\t" << "-d, --debug" << "\t" << "Debug mode." << endl;
	cout << "Examples:" << endl;
	cout << "\t" << "tp1 -d" << endl;
}

int parse_command_line(int argc, char **argv, unsigned int *debug) {
	char *cadena;

	for (int i = 1; i < argc; i++) {
		cadena = argv[i];
		if (cadena[0] == '-') {
			if (strcmp(cadena, "-d") == 0 || strcmp(cadena, "--debug") == 0) {
				*debug = 1;
			} else if (strcmp(cadena, "-h") == 0 || strcmp(cadena, "--help") == 0) {
				usage();
				return 1;
			} else if (strcmp(cadena, "-V") == 0 || strcmp(cadena, "--version") == 0) {
				cout << "TP1 - Version 1.0" << endl;
				cout << "Colinas, Sergio - 84189" << endl;
				cout << "Hernández, Duilio - 81253" << endl;

				return 1;
			} else {
				cerr << "tp1: " << cadena << " comando desconocido" << endl;
				cerr << "Pruebe './tp1 -h' para más información" << endl;

				return 1;
			}
		} else {
			usage();
			return 1;
		}
	}

	return 0;

}
